// We need our declaration
#include "CircularMenu.h"

namespace juce
{
    static const float Pi = 3.1415956535f;
    static const float iconAlpha = 0.6f;
    static const int dismissCommandId = 0x6287345f;
    static VoidArray activeMenuWindows;


    struct CircularMenuWindow  : public Component,
                                private Timer
    {
        enum
        {
            timerInterval = 10,
            maxNbSteps = 6,
            FadeIn          = 0,
            Unfold          = 1,
            UnfoldAndFadeIn = 2,
            ShowLabels      = 3,
            Static          = 4,
            FoldAndFadeOut  = 5,
        };

        uint32 menuCreationTime, lastFocused, lastScroll, lastMouseMoveTime, timeEnteredCurrentChildComp;
        CircularMenuWindow*         activeSubMenu;
        ComponentDeletionWatcher*   attachedCompWatcher;
        Component* const            componentAttachedTo;
        ApplicationCommandManager** managerOfChosenCommand;
        int lastMouseX, lastMouseY;
        ReduceOpacityEffect makeTransparent;
        GlowEffect    dropShadow;
        int         currentAnimationPhase;
        int         currentAnimationSteps;
        int         selectedItemID;
        Point       menuCenter;
        float       radius;


        Array<Label*>           labels;

        OwnedArray<Drawable> &  icons;
        Array<int> &            itemsID;
        StringArray &           itemsText;

        CircularMenuWindow(const int screenX, const int screenY, const int width, const int height,
                            OwnedArray<Drawable> & _icons, Array<int> & _itemsID, StringArray & _itemsText, ApplicationCommandManager** _managerOfChosenCommand, Component* const _componentAttachedTo)
            : icons(_icons),
              itemsID(_itemsID),
              itemsText(_itemsText),
              activeSubMenu(0),
              attachedCompWatcher(0),
              makeTransparent(0),
              selectedItemID(0),
              radius((float)width / 2),
              managerOfChosenCommand(_managerOfChosenCommand),
              componentAttachedTo(_componentAttachedTo),
              currentAnimationPhase(4),
              currentAnimationSteps(0)
               
             
        {
            menuCreationTime = lastFocused = lastScroll = Time::getMillisecondCounter();
            setWantsKeyboardFocus (true);

            attachedCompWatcher = componentAttachedTo != 0 ? new ComponentDeletionWatcher (componentAttachedTo) : 0;

            setOpaque (false);
            setAlwaysOnTop (true);

            Desktop::getInstance().addGlobalMouseListener (this);

            activeMenuWindows.add (this);

            Rectangle rect;
            // At first, the petal are all transparent
            // Then add and dispatch the petals
            for (int i = 0; i < itemsID.size(); i++)
            {
                // Create the petal now
                Colour  baseColour = Colours::mediumpurple;
                CircularMenu::MenuPetal * petal = new CircularMenu::MenuPetal(itemsID.size(), *icons[i], itemsID[i], baseColour.withRotatedHue((float)i / (float)itemsID.size()), false);
                petal->resizeToFit((float)width/2, (float)i * 2 * 3.1415926535f / itemsID.size());
                if (i == 0) rect = petal->getBounds();
                else rect = rect.getUnion(petal->getBounds());
                     
                // Then rotate the petal to the final position (will be removed afterward)
                // petal->rotateAround((float)width / 2, (float)height/2, (float)i * 2 * 3.1415926535f / itemsID.size());
                // Then add this object
                addAndMakeVisible(petal);
                // Set the component effect
                petal->setComponentEffect(&makeTransparent);
            }


           
            addToDesktop (ComponentPeer::windowIsTemporary);
            setBounds(screenX /*+ rect.getX()*/, screenY /*+ rect.getY()*/, width, height);
            menuCenter.setXY((float)width / 2, (float)height / 2);

            Rectangle currentBounds = getBounds();
            Point topLeft((float)currentBounds.getX(), (float)currentBounds.getY());
            for (int i = 0; i < itemsID.size(); i++)
            {
                // Create the label at first
                Label * label = new Label(String("MenuLabel") << i, itemsText[i]);
                if (i == 0)
                {
                    Font labelFont = label->getFont();
                    labelFont.setBold(true);
                    label->setFont(labelFont);
                }

                // Then compute the required label size
                float labelHeight = label->getFont().getHeight() + 2.0f;
                float labelWidth = label->getFont().getStringWidthFloat(label->getText()) + 5.0f;

                // Compute the center point now
                float x = radius + labelWidth / 2 + 5.0f;
                float y = 0;

                // Rotate it for the given petal
                const AffineTransform & rotationUsed = AffineTransform::rotation((float)i * 2 * Pi / itemsID.size());
                rotationUsed.transformPoint(x, y);

                // Then translate to the current origin
                x += menuCenter.getX();
                y += menuCenter.getY();

                // Then add it to the array and child component
                labels.add(label);
                // Define the current label bound
                label->setBounds(roundFloatToInt(x - labelWidth/2), roundFloatToInt(y - labelHeight/2), roundFloatToInt(labelWidth), roundFloatToInt(labelHeight));

                currentBounds = currentBounds.getUnion(label->getBounds().translated(roundFloatToInt(topLeft.getX()), roundFloatToInt(topLeft.getY())));
            }
            // Then, we need to enlarge our current bounds to include the labels
            menuCenter.setXY(menuCenter.getX() + (topLeft.getX() - currentBounds.getX()), menuCenter.getY() + (topLeft.getY() - currentBounds.getY()));
            setBounds(currentBounds);

            // Now move every petal to their respective positions
            for (int i = 0; i < itemsID.size(); i++)
            {
                Point componentCenter((float)(getChildComponent(i)->getX() + getChildComponent(i)->getWidth() / 2), (float)(getChildComponent(i)->getY() + getChildComponent(i)->getHeight() / 2));
				getChildComponent(i)->setCentrePosition(roundFloatToInt(componentCenter.getX() + topLeft.getX() - currentBounds.getX()), roundFloatToInt(componentCenter.getY() + topLeft.getY() - currentBounds.getY()));
                CircularMenu::MenuPetal * menu = dynamic_cast<CircularMenu::MenuPetal*>(getChildComponent(i));
                menu->setGeneralOffset(roundFloatToInt(topLeft.getX() - currentBounds.getX()), roundFloatToInt(topLeft.getY() - currentBounds.getY()));
            }

            // And adjust the label final bounds too
            for (int i = 0; i < labels.size(); i++)
            {
                Label * label = labels[i];
                Point componentCenter((float)(label->getX() + label->getWidth() / 2), (float)(label->getY() + label->getHeight() / 2));
                label->setCentrePosition(roundFloatToInt(componentCenter.getX() + topLeft.getX() - currentBounds.getX()), roundFloatToInt(componentCenter.getY() + topLeft.getY() - currentBounds.getY()));
                // Stupid hack to avoid popping artefact
                addChildComponent(label);
//                label->setComponentEffect(&dropShadow);
                label->setVisible(false);
                label->addMouseListener(this, false);
            }

            //dropShadow.setShadowProperties(2, 0.8f, 1, 1);
            dropShadow.setGlowProperties(6.0f, Colours::white);
            setVisible(true);
            // Unfold the petal now
            unfoldPetals();
        }

        ~CircularMenuWindow()
        {
            activeMenuWindows.removeValue (this);

            Desktop::getInstance().removeGlobalMouseListener (this);
            jassert (activeSubMenu == 0 || activeSubMenu->isValidComponent());
            delete activeSubMenu;
           
            deleteAllChildren(); delete attachedCompWatcher;
        }
        void paint(Graphics& g)
        {
//            g.drawRect(menuCenter.getX() - 2, menuCenter.getY() - 2, 4, 4);
        }

        void unfoldPetals()
        {
            currentAnimationPhase = UnfoldAndFadeIn;
            currentAnimationSteps = 0;
            startTimer(timerInterval);
        }

        void foldPetalsAndExit(const int selectedId)
        {
            stopTimer();
            currentAnimationSteps = 0;
            currentAnimationPhase = FoldAndFadeOut;
            selectedItemID = selectedId;
            startTimer(timerInterval);
        }

        void mouseEnter (const MouseEvent& e)
        {
            // Find which label this event is relative too
            Label * label = dynamic_cast<Label*>(e.eventComponent);
            if (label)
            {
                // Ok, now find the label index
                for (int i = 0; i < itemsText.size(); i++)
                {
                    ((CircularMenu::MenuPetal*)getChildComponent(i))->makeSelected(itemsText[i] == label->getText());
                }
            }
        }
        /** Called when mouse leaves this petal */
        void mouseExit (const MouseEvent& e)
        {
            // Find which label this event is relative too
            Label * label = dynamic_cast<Label*>(e.eventComponent);
            if (label)
            {
                // Ok, now find the label index
                for (int i = 0; i < itemsText.size(); i++)
                {
                    if (itemsText[i] == label->getText())
                        ((CircularMenu::MenuPetal*)getChildComponent(i))->makeSelected(false);
                }
            }
        }
        /** Called when mouse button hasd been clicked on this petal */
        void mouseUp (const MouseEvent& e)
        {
            // Find which label this event is relative too
            Label * label = dynamic_cast<Label*>(e.eventComponent);
            if (label)
            {
                // Ok, now find the label index
                for (int i = 0; i < itemsText.size(); i++)
                {
                    if (itemsText[i] == label->getText())
                        foldPetalsAndExit(itemsID[i]);
                }
            }
        }
       

        bool isOverAnyMenu()
        {
            return false;
        }

        void inputAttemptWhenModal()
        {
            timerCallback();

            if (! isOverAnyMenu())
            {
                if (componentAttachedTo != 0 && ! attachedCompWatcher->hasBeenDeleted())
                {
                    // we want to dismiss the menu, but if we do it synchronously, then
                    // the mouse-click will be allowed to pass through. That's good, except
                    // when the user clicks on the button that orginally popped the menu up,
                    // as they'll expect the menu to go away, and in fact it'll just
                    // come back. So only dismiss synchronously if they're not on the original
                    // comp that we're attached to.
                    int mx, my;
                    componentAttachedTo->getMouseXYRelative (mx, my);

                    if (componentAttachedTo->reallyContains (mx, my, true))
                    {
                        postCommandMessage (dismissCommandId); // dismiss asynchrounously
                        return;
                    }
                }
                this->foldPetalsAndExit(0);
            }
        }

        void handleCommandMessage (int commandId)
        {
            Component::handleCommandMessage (commandId);

            if (commandId == dismissCommandId)
                foldPetalsAndExit (0);
        }


        void triggerCurrentlyHighlightedItem()
        {
            // Find the currently selected petal
            int i = 0, currentSelection = -1;
            for (i = 0; i < itemsID.size(); i++)
            {
                CircularMenu::MenuPetal * petal = dynamic_cast<CircularMenu::MenuPetal*>(getChildComponent(i));
                if (petal && petal->isSelected())
                {
                    currentSelection = i;
                    break;
                }
            }
            if (currentSelection < 0 || currentSelection >= itemsID.size()) currentSelection = 0; 
               
            foldPetalsAndExit(itemsID[currentSelection]);
        }

        void selectNextItem (const int delta)
        {
            // Find the currently selected petal
            int i = 0, currentSelection = -1;
            for (i = 0; i < itemsID.size(); i++)
            {
                CircularMenu::MenuPetal * petal = dynamic_cast<CircularMenu::MenuPetal*>(getChildComponent(i));
                if (petal && petal->isSelected())
                {
                    currentSelection = i;
                    break;
                }
            }

            // Then adjust the selection
            currentSelection += delta;
            if (currentSelection < 0) currentSelection = itemsID.size() - 1;
            if (currentSelection >= itemsID.size()) currentSelection = 0;

            // And apply
            for (i = 0; i < itemsID.size(); i++)
            {
                CircularMenu::MenuPetal * petal = dynamic_cast<CircularMenu::MenuPetal*>(getChildComponent(i));
                if (petal) petal->makeSelected(i == currentSelection);
            }
        }

        bool keyPressed (const KeyPress& key)
        {
            if (key.isKeyCode (KeyPress::downKey))
            {
                selectNextItem (1);
            }
            else if (key.isKeyCode (KeyPress::upKey))
            {
                selectNextItem (-1);
            }
            else if (key.isKeyCode (KeyPress::returnKey))
            {
                triggerCurrentlyHighlightedItem();
            }
            else if (key.isKeyCode (KeyPress::escapeKey))
            {
                foldPetalsAndExit(0);
            }
            else
            {
                return false;
            }

            return true;
        }

        void timerCallback()
        {
            if (!isVisible()) return;

            if (attachedCompWatcher != 0 && attachedCompWatcher->hasBeenDeleted())
            {
                //dismissMenu (0);
                return;
            }

            switch(currentAnimationPhase)
            {
            case FadeIn:  // We make the petal appear
                {
                    // The number of steps in this phase is 10
                    makeTransparent.setOpacity((float)(++currentAnimationSteps) / (float)maxNbSteps);
                    for (int i = 0; i < itemsID.size(); i++)
                    {
                        this->getChildComponent(i)->repaint();
                    }
                    if (currentAnimationSteps == maxNbSteps)
                    {
                        currentAnimationSteps = 0;
                        currentAnimationPhase = Unfold;
                    }
                    break;
                }
            case Unfold:  // We rotate petals
                {
                    currentAnimationSteps++;
                    for (int i = 0; i < itemsID.size(); i++)
                    {
                        CircularMenu::MenuPetal * petal = dynamic_cast<CircularMenu::MenuPetal*>(this->getChildComponent(i));
                        if (petal)
                            petal->rotateAround(menuCenter.getX(), menuCenter.getY(), (float)i * 2 * Pi * (float)(currentAnimationSteps / (float)maxNbSteps) / itemsID.size());
                    }
                    if (currentAnimationSteps == maxNbSteps)
                    {
                        currentAnimationSteps = 0;
                        currentAnimationPhase = ShowLabels;
                    }
                }
                break;
            case UnfoldAndFadeIn:  // We both make the petal appear and rotate
                {
                    // The number of steps in this phase is 10
                    makeTransparent.setOpacity((float)(++currentAnimationSteps) / (float)maxNbSteps);
                    for (int i = 0; i < itemsID.size(); i++)
                    {
                        CircularMenu::MenuPetal * petal = dynamic_cast<CircularMenu::MenuPetal*>(this->getChildComponent(i));
                        if (petal)
                            petal->rotateAround(menuCenter.getX(), menuCenter.getY(), (float)i * 2 * Pi * (float)(currentAnimationSteps  / (float)maxNbSteps) / itemsID.size());
                    }
                    if (currentAnimationSteps == maxNbSteps)
                    {
                        currentAnimationSteps = 0;
                        currentAnimationPhase = ShowLabels;
                    }
                    break;
                }
            case ShowLabels:
                {
                    // Try to work out in 3 phases
//                    static Array<Component*> desktopComponents;
                    for (int i = 0; i < labels.size(); i++)
                    {
/*
                        labels[i]->setColour(Label::backgroundColourId, Colours::white);
                        labels[i]->setColour(Label::outlineColourId, Colours::transparentWhite);
                        labels[i]->setColour(Label::textColourId, Colours::black);
                        */
                        labels[i]->setComponentEffect(&dropShadow);

                        labels[i]->setVisible(true);

                    }

                    currentAnimationSteps = 0;
                    currentAnimationPhase = Static;
                }
                break;

            case Static: // This is static step
                break;

            case FoldAndFadeOut: // We fold and make petal disappear
                {
                    // The number of steps in this phase is 10
                    makeTransparent.setOpacity(1.0f - (float)(++currentAnimationSteps) / (float)maxNbSteps);
                    for (int i = 0; i < itemsID.size(); i++)
                    {
                        CircularMenu::MenuPetal * petal = dynamic_cast<CircularMenu::MenuPetal*>(this->getChildComponent(i));
                        if (petal)
                            petal->rotateAround(menuCenter.getX(), menuCenter.getY(), (float)i * 2 * Pi * (1.0f - (float)(currentAnimationSteps  / (float)maxNbSteps)) / itemsID.size());
                    }
                    if (currentAnimationSteps == maxNbSteps)
                    {
                        currentAnimationSteps = 0;
                        currentAnimationPhase = Static;
                        exitModalState(selectedItemID);
                    }
                    break;
                }
            }

            int mx, my;
            Desktop::getMousePosition (mx, my);

            int x = mx, y = my;
            globalPositionToRelative (x, y);

            const uint32 now = Time::getMillisecondCounter();
/*
            if (now > timeEnteredCurrentChildComp + 100
                 && reallyContains (x, y, true)
                 && currentChild->isValidComponent()
                 && (! disableMouseMoves)
                 && ! (activeSubMenu != 0 && activeSubMenu->isVisible()))
            {
                showSubMenuFor (currentChild);
            }
*/
        }

 
    };




    //==================================== Petals
    CircularMenu::MenuPetal::MenuPetal(const int nbPetal, Drawable & _icon, const int _itemID, const Colour & _baseColour, const bool hasSubMenu)
        : icon(_icon), baseColour(_baseColour.withAlpha(0.4f)), withSubMenu(hasSubMenu),
          surfaceAngle(2 * Pi / (float)nbPetal), itemID(_itemID), isPreSelected(false),
          currentAngle(0), petalRadius(0), iconImage(0), generalOffsetX(0), generalOffsetY(0)
    {
    }

    CircularMenu::MenuPetal::~MenuPetal()
    {
        delete iconImage;
        deleteAllChildren();
    }

    void CircularMenu::MenuPetal::resizeToFit(const float radius, const float finalAngle)
    {
        setCircleRadius(radius);

        // This part need work too
        // Then move the label now too
//        label->setBounds(roundFloatToInt(radius * 2), roundFloatToInt(radius - label->getFont().getHeight() * 0.5f), label->getFont().getStringWidth(label->getText()) + 5, roundFloatToInt(label->getFont().getHeight()));
//        Rectangle labelBounds = label->getBounds();
        float x, y, w, h;
        petalPath.getBounds(x, y, w, h);
        Rectangle petalBounds(roundFloatToInt(x), roundFloatToInt(y), roundFloatToInt(w), roundFloatToInt(h));
        topLeftCorner.setXY(-x, -y);

        setBounds(petalBounds.translated(generalOffsetX, generalOffsetY));

        // Then cache the icon image
        delete iconImage;
        // Get the icon bounds
        const Rectangle & iconBounds = getIconBounds(finalAngle);
        iconImage = new Image(Image::ARGB, iconBounds.getWidth(), iconBounds.getHeight(), false);
        iconImage->clear(0, 0, iconBounds.getWidth(), iconBounds.getHeight(), Colours::transparentWhite);
        Graphics g(*iconImage);
        icon.drawWithin(g, 0, 0, iconBounds.getWidth(), iconBounds.getHeight(), RectanglePlacement(RectanglePlacement::fillDestination | RectanglePlacement::centred | RectanglePlacement::stretchToFit));
        iconTopLeftCorner.setXY((float)iconBounds.getX(), (float)iconBounds.getY());
        iconImage->multiplyAllAlphas(iconAlpha);
    }

    void CircularMenu::MenuPetal::setCircleRadius(const float radius)
    {
        // We need to create the path for this petal now
        petalPath.clear();
        Path workingPath;

        workingPath.addPieSegment(0, 0, 2*radius, 2*radius, -surfaceAngle / 2 + Pi*0.5f, surfaceAngle/2 + Pi*0.5f, 0.1f);
//        workingPath.addRectangle(0, 0, radius, radius);
        petalPath = workingPath.createPathWithRoundedCorners(radius * 0.2f);
//        petalPath.applyTransform(AffineTransform::translation(-radius, -radius));


        // Ok, done
        petalRadius = radius;
    }

    inline float squareDistanceBetween(const Point & a, const Point & b)
    {
        return (a.getX() - b.getX())* (a.getX() - b.getX()) + (a.getY() - b.getY()) * (a.getY() - b.getY());
    }

    const Rectangle CircularMenu::MenuPetal::getIconBounds(const float angle) const
    {
        // What is the biggest dimension for this drawable ?
        float ix, iy, iw, ih;
        icon.getBounds(ix, iy, iw, ih);

        // Compute the maximum height, once we know the aspect ratio for the drawable
        double ar = iw / ih;
        double lambda = 0.5f * cos(surfaceAngle/2) + ar;
        double maximumHeight = (double)petalRadius / lambda * cos (atan(0.5f / lambda));
       
        // Once we have the maximum height, we can deduce the maximum width
        double maximumWidth = maximumHeight * ar;
        double minHorizontalPositionForIcon = cos(surfaceAngle / 2) * maximumHeight / 2;
        double minVerticalPositionForIcon = (float)petalRadius - maximumHeight/2;

        // Need to rotate this center point too
        float x = (float)(minHorizontalPositionForIcon + maximumWidth / 2);
        float y = 0;
        AffineTransform::rotation(angle == -1 ? currentAngle : angle).transformPoint(x, y);

        // Because we always draw the icons straight up, the bounding rectangle
        // could overflow the circle limit
        // So check this case, and deduce the size factor
        double sizeFactor = 0.9f;
        // Find the icon bottom left corner position
        Point blc(x - (float)maximumWidth / 2, y + (float)maximumHeight / 2);
        Point tlc(x - (float)maximumWidth / 2, y - (float)maximumHeight / 2);
        Point trc(x + (float)maximumWidth / 2, y - (float)maximumHeight / 2);
        Point brc(x + (float)maximumWidth / 2, y + (float)maximumHeight / 2);
        // Check if any point is outside the circle
        float squareRadius = petalRadius * petalRadius;
        Point center(0, 0);
        float distance = squareDistanceBetween(center, blc);
        float maxDistance = distance;
        distance = squareDistanceBetween(center, brc);
        maxDistance = jmax(maxDistance, distance);
        distance = squareDistanceBetween(center, trc);
        maxDistance = jmax(maxDistance, distance);
        distance = squareDistanceBetween(center, tlc);
        maxDistance = jmax(maxDistance, distance);

        if (maxDistance * sizeFactor * sizeFactor > squareRadius)
        {
            // Need to resize the value to match the given distance
            sizeFactor *= petalRadius / sqrt(maxDistance);
        }

        // And finally compute the icon bounds
        return Rectangle(roundDoubleToInt(x + topLeftCorner.getX() - maximumWidth / 2 + maximumWidth * (1.0f - sizeFactor) * 0.5f + petalRadius), roundDoubleToInt(y + topLeftCorner.getY() + petalRadius - maximumHeight / 2 + maximumHeight * (1.0f - sizeFactor) * 0.5f), roundDoubleToInt(maximumWidth * sizeFactor), roundDoubleToInt(maximumHeight * sizeFactor));
    }

    bool CircularMenu::MenuPetal::hitTest(int x, int y)
    {
        // Check if we are inside the petal area
        return petalPath.contains(x - topLeftCorner.getX(), y - topLeftCorner.getY());
    }


    void CircularMenu::MenuPetal::paint(Graphics& g)
    {   
        //g.fillAll(Colours::grey);
        float x, y, w, h;
        petalPath.getBounds(x, y, w, h);
        Rectangle petalBounds(roundFloatToInt(0), roundFloatToInt(0), roundFloatToInt(w), roundFloatToInt(h));

        x = petalRadius * 0.1f; y = 0;
        AffineTransform rotationUsed = AffineTransform::rotation(currentAngle);
        rotationUsed.transformPoint(x, y);
        Point gradientPoint1(x + topLeftCorner.getX() + petalRadius, y + topLeftCorner.getY() + petalRadius);
        x = petalRadius * 0.95f; y = 0;
        rotationUsed.transformPoint(x, y);
        Point gradientPoint2(x + topLeftCorner.getX() + petalRadius, y + topLeftCorner.getY() + petalRadius);

        // The hardest part, I guess
        Colour workingColour = baseColour;
        GradientBrush gradient_2 (workingColour.withMultipliedAlpha(0.16f),
                              gradientPoint1.getX(), gradientPoint1.getY(),
                              workingColour.withMultipliedAlpha(1.0f),
                              gradientPoint2.getX(), gradientPoint2.getY(),
                              false);
        g.setBrush (&gradient_2);
        // Fill the petal path
        Path workingPath(petalPath);
        workingPath.applyTransform(AffineTransform::translation(topLeftCorner.getX(), topLeftCorner.getY()));
        g.fillPath(workingPath);
        // And the outline too
//        g.setColour (Colour (0x661b1b1b));
        workingColour = workingColour.darker();
        GradientBrush gradient_3 (workingColour.withMultipliedAlpha(0.04f),
                              gradientPoint1.getX(), gradientPoint1.getY(),
                              workingColour.withMultipliedAlpha(1.0f),
                              gradientPoint2.getX(), gradientPoint2.getY(),
                              false);
        g.setBrush (&gradient_3);
        g.strokePath (workingPath, PathStrokeType (1.0000f));

        // Then draw the drawable too
        // This is a little bit more complex here
       
       
        // We reduce the icon a little bit too to give free space on it (so it's more pleasant)
        g.drawImageAt(iconImage, roundFloatToInt(iconTopLeftCorner.getX()), roundFloatToInt(iconTopLeftCorner.getY()));
    }
   
    void CircularMenu::MenuPetal::rotateAround(const float _x, const float _y, const float angle)
    {
        // Undo the previous rotation
        petalPath.applyTransform(AffineTransform::rotation(-currentAngle, pivot.getX(), pivot.getY()));
        // Then rotate
        pivot.setXY(_x - generalOffsetX, _y - generalOffsetY);
        currentAngle = angle;
        petalPath.applyTransform(AffineTransform::rotation(currentAngle, pivot.getX(), pivot.getY()));

        {
            float x, y, w, h;
            petalPath.getBounds(x, y, w, h);
            Rectangle petalBounds(roundFloatToInt(x), roundFloatToInt(y), roundFloatToInt(w), roundFloatToInt(h));
            topLeftCorner.setXY(-x, -y);

            setBounds(petalBounds.translated(generalOffsetX, generalOffsetY));
            const Rectangle & iconBounds = getIconBounds(angle);
            iconTopLeftCorner.setXY((float)iconBounds.getX(), (float)iconBounds.getY());
        }
    }

    void CircularMenu::MenuPetal::mouseUp(const MouseEvent& e)
    {
        // Dismiss the parent modal loop
        CircularMenuWindow * window = dynamic_cast<CircularMenuWindow*>(getParentComponent());
        if (window)
            window->foldPetalsAndExit(itemID);
        else getParentComponent()->exitModalState(itemID);
    }

    void CircularMenu::MenuPetal::mouseEnter(const MouseEvent& e)
    {
        makeSelected(true);
    }

    void CircularMenu::MenuPetal::mouseExit(const MouseEvent& e)
    {
        makeSelected(false);
    }

    void CircularMenu::MenuPetal::setGeneralOffset(const int x, const int y)
    {
        generalOffsetX = x;
        generalOffsetY = y;
    }

    void CircularMenu::MenuPetal::makeSelected(const bool shouldBeSelected)
    {
        if (!isPreSelected && shouldBeSelected)
        {
            baseColour = baseColour.withAlpha(1.0f);
            if (iconImage) iconImage->multiplyAllAlphas(1.0f/iconAlpha);
            repaint();
            isPreSelected = true;
        }
        else if (isPreSelected && !shouldBeSelected)
        {
            baseColour = baseColour.withAlpha(0.4f);
            if (iconImage) iconImage->multiplyAllAlphas(iconAlpha);
            repaint();
            isPreSelected = false;
        }
    }

    bool CircularMenu::MenuPetal::isSelected() { return isPreSelected; }

    //===================================== Menu
    bool CircularMenu::addItem(const int itemResultId, Drawable* iconToUse, const String& itemText)
    {
        if (!iconToUse) return false;
        if (itemsId.contains(itemResultId)) return false;
        itemsId.add(itemResultId);
        icons.add(iconToUse);
        itemTexts.add(itemText);
        return true;
    }

    int CircularMenu::show(const int minimumRadius)
    {
        // Compute the radius
        int x, y;
        Desktop::getMousePosition (x, y);


        return showAt(x, y, minimumRadius);
    }



    Component* CircularMenu::createMenuComponent (const int x, const int y, const int w, const int h, ApplicationCommandManager** managerOfChosenCommand, Component* const componentAttachedTo) throw()
    {
        CircularMenuWindow * window = new CircularMenuWindow(x, y, w, h, icons, itemsId, itemTexts, managerOfChosenCommand, componentAttachedTo);
        if (window) window->setVisible(true);
        return window;
    }


    int CircularMenu::showAt(const int screenX, const int screenY, const int minimumRadius)
    {
        // Save the previously focused item to restore after this call
        Component* const prevFocused = Component::getCurrentlyFocusedComponent();
        // Also save the top level component
        Component* const prevTopLevel = (prevFocused != 0) ? prevFocused->getTopLevelComponent() : 0;

        // We want to be informed if the previous focus component is being deleted
        ComponentDeletionWatcher* deletionChecker1 = 0;
        if (prevFocused != 0) deletionChecker1 = new ComponentDeletionWatcher (prevFocused);
        ComponentDeletionWatcher* deletionChecker2 = 0;
        if (prevTopLevel != 0) deletionChecker2 = new ComponentDeletionWatcher (prevTopLevel);

        bool wasHiddenBecauseOfAppChange = false;

        int result = 0;
        ApplicationCommandManager* managerOfChosenCommand = 0;

        Component* const popupComp = createMenuComponent (screenX - minimumRadius, screenY - minimumRadius, minimumRadius * 2 , minimumRadius * 2,
                                                          &managerOfChosenCommand,
                                                          0);

        if (popupComp != 0)
        {
            popupComp->enterModalState (false);
            popupComp->toFront (false);  // need to do this after making it modal, or it could
                                         // be stuck behind other comps that are already modal..

            result = popupComp->runModalLoop();
            delete popupComp;

            if (! wasHiddenBecauseOfAppChange)
            {
                if (deletionChecker2 != 0 && ! deletionChecker2->hasBeenDeleted())
                    prevTopLevel->toFront (true);

                if (deletionChecker1 != 0 && ! deletionChecker1->hasBeenDeleted())
                    prevFocused->grabKeyboardFocus();
            }
        }

        delete deletionChecker1;
        delete deletionChecker2;

        if (managerOfChosenCommand != 0 && result != 0)
        {
            ApplicationCommandTarget::InvocationInfo info (result);
            info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;

            managerOfChosenCommand->invoke (info, true);
        }

        return result;
    }
} 